#!/usr/bin/perl
###############################################################################
# Version:  0.01a (28th August 2006)
# Author:  R2Hosting, Melbourne. Australia
# Contact: info@r2hosting.com.au
# FileName: checkJOSOBJVersionsNew.pl
# Required Modules: File::Find::Rule
#
# Information/Overview:
# This is a quick and (very) dirty check to find installed Joomla/Mambo
# ojects (Components, Modules, Mambots, Language, Plugins) user home_dir's.
# It makes use of the object .xml files to locate releavant information and 
# send the output to (optionally) STDOUT and to a designated email.
# Originally posted in the Joomla Forums, please post any modifications or
# improvements to assist others in the promotion and execution of Joomla 
# and the Open Source cause.
###############################################################################
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later 
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
# more details.
###############################################################################

use POSIX;
use File::Find::Rule;
use MIME::Lite;

# Choose Your Options Here.
# Logic says that at least one of these ($outputresults or emailresults) should be set to 1  


$outputresults          =       '1';                                      # Do you want to Output Results to STDOUT? (Default 0)

$emailresults           =       '1';                                      # Do you want to Email the Results? (Default 1)
                                                                          # If emailresults = 1, then $admin_email must be
                                                                          # a valid email address.

$admin_email            =       'youremail@yourdomain.com.au';            # Enter Your E-Mail Address

$emailsubject           =       'Joomla/Mambo Installed Objects Audit';   # Enter the Subject you want, we will
                                                                          # append hostname and date regardless

$show_core              =       '0';                                      # Show CORE Objects as well as CUSTOM objects? (Default 0)
                                                                          # Objects distributed with Joomla!/Mambo are
                                                                          # considered CORE, the objects you install into
                                                                          # Joomla!/Mambo are considered CUSTOM

$uid_from               =       '501';                                    # These settings are required to auto-select the 
$uid_to                 =       '65001';                                  # appropriate user_home directories from /etc/passwd
                                                                          # ignoring standard users. 
                                                                          # On most unix systems hosting users start quit alot 
                                                                          # higher then system users and there maybe standard
                                                                          # in the higher than ranges (nornally 65000 >)
                                                                          # The defaults should cover a variety of hosting/server
                                                                          # /control panel conventions but you may wish to 
                                                                          # specifically state ranges reducing search overhead. 
                                                                          # (Defaults $uid_to = 501 or 32001 & $uid_from = 65001)

#####################################################################################
# SECURITY NOTE: SHARED HOSTING USERS                                               #
# If you are a sinlge installation, shared hosting user, the script will auto-      #
# select your own UiD from /etc/passwd (as long as the range you entered above      #
# actually includes your UiD. As long as the system permissions and security is     #
# is at least default for the /etc directory, you will not be able to see other     #
# non-system users.                                                                 #
#####################################################################################

setpriority(PRIO_PROCESS,0,19);                                            # Sets this Scripts Priority (20 lowest, -20 highest)

#####################################################################################
# END OF USER DEFINABLE OPTIONS                                                     #
# Continue at your own peril...                                                     #
#####################################################################################




# Define my variables
my $findfilename;
my $type;
my $title;
my $version;
my $created;
my $author;
my $authurl;
my $desc;


# Start Indexing and Searching home_dir's
if($outputresults == 1){ print "Preparing Search...\nStarting Search, This may take a while...\n\n"; }

# This is the Joomla/Mambo default metaData file type
$findfilename = '*.xml'; 



####################################################################################
# This is where we cruise through /etc/passwd to find the users home_directory     #
# We select a range of UiD's to avoid including non-hosting end-users.             #
####################################################################################

      open(passwd, '/etc/passwd');
      while (<passwd>) {
        ($login, $passwd, $uid, $gid, $gcos, $home, $shell)
                = split(/:/);

      if(($uid > $uid_from) && ($uid < $uid_to)) {
        $searchpath = "$home";
      }
  
  if($outputresults == 1) { print "$searchpath Complete... "; }

    my @files = File::Find::Rule->file
                            ->name("$findfilename")
                            ->in("$searchpath");


foreach $file (@files){
        undef $type;
        undef $title;
        undef $version;
        undef $created;
        undef $author;
        undef $authurl;
        undef $desc;
        open(FM,"$file");
        while (<FM>) {

                # Look for the Object type (component, Module etc)
                if(m/\s*.*<mosinstall\stype=\"(\w*)\"/x) { $type = $1; }
                if(m/\s*.*<mosinstall\s*.*type=\"(\w*)\"/x) { $type = $1; }

                # Look for the Objects name/title
                if(m/\s*.*<name>(.*)\s*<\/name>/x){ $title = $1; } 
                if(m/\s*.*<title>(.*)\s*<\/title>/x){ $title = $1; } 

                # Look for the Objects version information
                if(m/\s*.*<version>(.*)\s*<\/version>/x) { $version = $1; }

                # Look for the Objects creation/build date
                if(m/\s*.*<creationDate>(.*)\s*<\/creationDate>/x) { $created = $1; }

                # Look for the Author details
                if(m/\s*.*<author>(.*)\s*<\/author>/x) { $author = $1; }

                # Look for the Authors URL
                if(m/\s*.*<authorUrl>(.*)\s*<\/authorUrl>/x) { $authurl = $1; }

                # Look for any Description of the Object
                if(m/\s*.*<description>(.*)\s*<\/description>/x) { $desc = $1; last; }

        } # close while
        close(FM);

# Lame attempt to only capture Joomla/Mambo Object files, if the xml file does not contain
# an Object type, ignore it. This means that we might miss some Objects that dont self-describe.
# On the good side though, this means we dont go picking up other unrelated xml files or the 
# help files.
if ($type eq "") { } else {

  # If $show_core is 0 (Off) then attempt to Exclude Core Files. This is not fool-proff, but it works.
  # The logic is very simple, those objects listed in either Author or Author URL as being from the 
  # Joomla! Project or the Mambo Project will be Excluded from the output.
  if (($show_core == 0) && (($authurl eq "www\.joomla\.org") || ($author eq "Joomla! Project") || ($authurl eq "www\.mamboserver\.com"))) { } else {

               push @BADVERSIONS, "$file~$type~$title~$version~$created~$author~$authurl~$desc";
               # " ~ " delimeter used instead of the usual " : " due to the use of " : " in several xml fields.

  }
}


} #close for



}

# OK, by now we should be done and are ready to output to STDOUT and/or email.
if ($outputresults == 1) { print "\n\nFinished Indexing and Searching...\nPreparing Output...\n\n"; }

# did you set your choices at the top?
if($emailresults == 1){ sendmail(); }
if($outputresults == 1){ displayresults(); }

### SUBS ###
sub sendmail {
        $hostname = `hostname`;
        chomp($hostname);
        $today = `date`;
        chomp($today);


        foreach (@BADVERSIONS){
                ($file,$objtype,$objtitle,$objversion,$objcreated,$objauthor,$objauthurl,$objdesc) = split("~", $_, 8);

                # If Joomla! Project is the Author or www.joomla.org is the Author URL it is determined to be a CORE Object
                # If www.mamboserver.com is the Author URL, it is determined to be a LEGACY Object
                # Other wise, it is determined to be a CUSTOM Object
                if(($objauthor eq "Joomla! Project") || ($objauthurl eq "www\.joomla\.org")) {
                  $objcore = "CORE ";
                    } elsif ($objauthurl eq "www\.mamboserver\.com") { 
                      $objcore = "LEGACY ";
                    } else {
                      $objcore = "CUSTOM ";
                }

                # Pretty up the output, UPPERCASE/HIGHLITE the Object Type
                $objtype =~ tr/a-z/A-Z/;

                $instances .= "    -----------------------------------------------------------------------------------\n";
                $instances .= "    $objcore$objtype: $objtitle  Ver: $objversion  Date: $objcreated\n";
                $instances .= "    Author: $objauthor ($objauthurl)\n";

                # For email, truncate the description field if larger than 60 chars
                if($objdesc lt 60) {
                    $instances .= "    Description: $objdesc\n";
                      } else {
                        $shortdesc = substr($objdesc, 0, 60);
                        $instances .= "    Description: $shortdesc...\n";
                     }

                # Remove the xml filename from the Objects location
                ($newFile = $file) =~ s/\/$findfilename//ig;
                $instances .= "    $newFile\n";
                $instances .= "    ===================================================================================\n\n";
        }


        $subject = "$emailsubject ($hostname) $today\n";
        $msg = "\n\n$today\nThe following installations were found...\n\n";
          if($show_core == 0) {
              $msg .= "   Show Core Objects = No, Showing Custom Objects only.\n\n";
            } else {
              $msg .= "   Show Core Objects = Yes, Showing Core and Custom Objects.\n\n";
         }

        open(SENDMAIL,"|/usr/sbin/sendmail -t");
        print SENDMAIL "To: <$admin_email>\n";
        print SENDMAIL "From: root\@$hostname\n";
        print SENDMAIL "Subject: $subject\n\n";
        print SENDMAIL "$msg $instances";
        close(SENDMAIL);
}


sub displayresults {

        foreach (@BADVERSIONS){
                ($file,$objtype,$objtitle,$objversion,$objcreated,$objauthor,$objauthurl,$objdesc) = split("~", $_, 8);

                # As above, determine the object type and designate it to a catagory. CORE, LEGACY or CUSTOM
                if($objauthor eq "Joomla! Project") {
                  $objcore = "CORE ";
                    } elsif ($objauthor eq "Mambo Project / Gerard Korsten / Rene Kreijveld") {
                      $objcore = "LEGACY ";
                    } else { 
                      $objcore = "CUSTOM "; 
                }

                # Pretty up the Object type (UPERCASE/HIGHLITE)
                $objtype =~ tr/a-z/A-Z/;
                print "\n";
                print "    $objcore$objtype: $objtitle  Ver: $objversion  Date: $objcreated\n";
                print "    Author: $objauthor  ($objauthurl)\n";

                # For STDOUT, truncate the description field to 80 chars
                if($objdesc lt 80) {
                    print "    Description: $objdesc\n"; 
                      } else {
                        $shortdesc = substr($objdesc, 0, 80);
                        print "    Description: $shortdesc...\n";
                     }
         
                # Remove the xml filename from the Object location
                ($newFile = $file) =~ s/\/$findfilename//ig;
                print "    $newFile\n";
                print "    --------------------------------------------------------------------------------------------------\n";
       }

               if($show_core == 0) {
                  print "\n    Show Core Objects?   = No,  Showing Custom Objects only.";
                } else {
                  print "\n    Show Core Objects?   = Yes, Showing Core and Custom Objects.";
               }

               if($emailresults == 0) {
                  print "\n    Email Admin Results? = No,  Results Not Emailed.\n\n";
                } else {
                  print "\n    Email Admin Results? = Yes, Results Emailed To $admin_email.\n\n";
               }

}

# RW 18.09.2006
# Minor edits, typos, faster search